package grpc

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	pb "gitee.com/zhucheer/orange/grpc/sweet"
	"gitee.com/zhucheer/orange/utils"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/keepalive"
	"sync"
	"time"
)

const version = "0.0.3"

// orange框架内部grpc调用客户端
type Client struct {
	srvAddr     string
	requestBody []byte
	host        string
	header      map[string]string
	timeout     int
	mutex       sync.RWMutex
}

type ClientResp struct {
	Body           []byte // response body
	ResponseStatus int
	RequestTime    time.Duration
}

var targetInstanceMap map[string]*grpc.ClientConn

func init() {
	targetInstanceMap = make(map[string]*grpc.ClientConn)
}
func (c *ClientResp) String() string {
	return string(c.Body)
}

func (c *ClientResp) GetRequestTime() time.Duration {
	return c.RequestTime
}

func NewClient(addr string) *Client {
	clientIp := utils.GetLocalIp()
	defaultHeader := make(map[string]string)
	defaultHeader["User-Agent"] = "OrangeGrpcClient/" + version
	defaultHeader["X-Client-Ip"] = clientIp
	return &Client{
		header:  defaultHeader,
		timeout: 10,
		srvAddr: addr,
	}
}

// Header 原样保持header头大小写设置
func (c *Client) Header(key, value string) *Client {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.header[key] = value
	return c
}

// 直接传递body中的参数
func (c *Client) WithBody(bodyStream string) *Client {
	c.requestBody = []byte(bodyStream)
	return c
}

// 参数设置 默认将结构体转成json
func (c *Client) WithParams(obj interface{}) *Client {
	body, _ := json.Marshal(obj)
	c.requestBody = body
	return c
}

// SetTimeout 设置超时
func (c *Client) SetTimeout(timeout int) *Client {
	c.timeout = timeout
	return c
}

// setTargetConn 设置一个grpc连接实例
func (c *Client) setTargetConn(conn *grpc.ClientConn) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	targetInstanceMap[c.srvAddr] = conn
	return
}

// getTargetConn 获取一个grpc连接实例
func (c *Client) getTargetConn() *grpc.ClientConn {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	conn, ok := targetInstanceMap[c.srvAddr]

	if ok {
		return conn
	}
	return nil
}

// RunGet 执行Post请求
func (c *Client) RunGRPC(apiUri string) (clientResp *ClientResp, err error) {
	clientResp = &ClientResp{}
	if c.srvAddr == "" {
		return clientResp, errors.New("grpc server address is empty")
	}
	// 超时设置
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.timeout)*time.Second)
	defer cancel()

	var response *pb.Response
	conn := c.getTargetConn()
	// 断开重连
	if conn == nil || conn.GetState() != connectivity.Ready {
		conn, err = grpc.DialContext(ctx, c.srvAddr, grpc.WithInsecure(), grpc.WithBlock(),
			grpc.WithKeepaliveParams(keepalive.ClientParameters{
				Time:                10 * time.Second,
				Timeout:             30 * time.Millisecond,
				PermitWithoutStream: true,
			}))
		c.setTargetConn(conn)
	}

	if err != nil {
		return
	}
	client := pb.NewOrangeRPCClient(conn)
	timeSt := time.Now()
	response, err = client.RequestDo(ctx, &pb.Parameter{Uri: apiUri, Body: c.requestBody, Header: c.header})

	if err != nil {
		return
	}

	requestTime := time.Now().Sub(timeSt)
	clientResp.ResponseStatus = int(response.Status)
	clientResp.Body = response.Body
	clientResp.RequestTime = requestTime
	return clientResp, err
}

// 关闭所有连接
func CloseAll() {
	fmt.Println("---------", "close all")
	for _, conn := range targetInstanceMap {
		if conn != nil {
			conn.Close()
		}
	}
}
